package com.m4f.docs.impl;


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.logging.Logger;
import com.google.gdata.client.DocumentQuery;
import com.google.gdata.client.docs.DocsService;
import com.google.gdata.data.DateTime;
import com.google.gdata.data.MediaContent;
import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.data.docs.DocumentEntry;
import com.google.gdata.data.docs.DocumentListEntry;
import com.google.gdata.data.docs.DocumentListFeed;
import com.google.gdata.data.docs.FolderEntry;
import com.google.gdata.data.extensions.LastModifiedBy;
import com.google.gdata.data.media.MediaByteArraySource;
import com.google.gdata.data.media.MediaSource;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.ServiceException;
import com.m4f.docs.ifc.IDocumentalService;

public class GoogleDocumentalService implements IDocumentalService {

	private static final Logger LOGGER = Logger.getLogger(GoogleDocumentalService.class.getName());
	
	private DocsService client = null;
	private static final String REPOSITORY_URL = "https://docs.google.com/feeds/default/private/full/";
	private final String URL_CATEGORY_DOCUMENT = "/-/document";
	private final String URL_CATEGORY_SPREADSHEET = "/-/spreadsheet";
	private final String URL_CATEGORY_PDF = "/-/pdf";
	private final String URL_CATEGORY_PRESENTATION = "/-/presentation";
	private final String URL_CATEGORY_STARRED = "/-/starred";
	private final String URL_CATEGORY_TRASHED = "/-/trashed";
	private final String URL_CATEGORY_FOLDER = "/-/folder";
	private final String URL_CATEGORY_EXPORT = "/Export";
	//private static final String EXPORT_URL = "https://docs.google.com/feeds/download/documents/Export";
	private static final String EXPORT_URL = "https://docs.google.com/feeds/download/documents/export/Export";
	private String applicationId; 
	private String username; 
	private String password;
	
	public GoogleDocumentalService(String applicationId, 
			String username, String password) throws AuthenticationException {
		this.applicationId = applicationId;
		this.username = username;
		this.password = password;
	}
	
	public void createCompanyContainer(String companyId) 
		throws IOException, ServiceException {
		synchronized(this) {
			if(client==null) {
				client = new DocsService(applicationId);
				client.setUserCredentials(username, password);
			}
		}
		DocumentListEntry newEntry = new FolderEntry();
		 newEntry.setTitle(new PlainTextConstruct(companyId));
		 URL feedUrl = new URL(REPOSITORY_URL);
		 client.insert(feedUrl, newEntry);
	}
	
	public void removeCompanyContainer(String companyId) 
		throws Exception {
		synchronized(this) {
			if(client==null) {
				client = new DocsService(applicationId);
				client.setUserCredentials(username, password);
			}
		}
		URL feedUri = new URL(REPOSITORY_URL + URL_CATEGORY_FOLDER);
		DocumentQuery query = new DocumentQuery(feedUri);
		query.setTitleQuery(companyId);
		query.setTitleExact(true);
		query.setMaxResults(1);
		DocumentListFeed feed = client.getFeed(query, DocumentListFeed.class);
		if(feed.getEntries().size()==0) {
			throw new Exception("Folder " + companyId + " NOT found in folder ");
		}
		feed.getEntries().get(0).delete();
	}
	
	public boolean existDocument(String companyId, String documentName) 
		throws IOException, ServiceException {
		synchronized(this) {
			if(client==null) {
				client = new DocsService(applicationId);
				client.setUserCredentials(username, password);
			}
		}
		DocumentListEntry documentEntry = this.findDocumentEntry(companyId, 
				documentName);
		if(documentEntry == null) {
			return false;
		}
		return true;
	}
	
	public void downloadPDF(String companyId, String documentName, 
			OutputStream outputStream) throws Exception {
		synchronized(this) {
			if(client==null) {
				client = new DocsService(applicationId);
				client.setUserCredentials(username, password);
			}
		}
		DocumentListEntry documentEntry = this.findDocumentEntry(companyId, 
				documentName);
		if(documentEntry == null) {
			throw new Exception("It's not posible find the document " + 
					documentName + " into folder " + companyId);
		}
		MediaContent mc = new MediaContent();
		String exportURL = EXPORT_URL + "?id=" + 
			documentEntry.getDocId() + "&format=pdf";
		mc.setUri(exportURL);
		MediaSource ms = client.getMedia(mc);
		InputStream inStream = null;
		try {
			inStream = ms.getInputStream();
			int c;
			while ((c = inStream.read()) != -1) {
				outputStream.write(c);
			}
		} finally {
			if (inStream != null) {
				inStream.close();
			}
			if (outputStream != null) {
				outputStream.flush();
				outputStream.close();
			}
		}
	}
	
	public void downloadDocument(String companyId, String documentName, 
			OutputStream outputStream) throws IOException, 
			MalformedURLException, ServiceException, Exception {
		synchronized(this) {
			if(client==null) {
				client = new DocsService(applicationId);
				client.setUserCredentials(username, password);
			}
		}
		DocumentListEntry documentEntry = this.findDocumentEntry(companyId, 
				documentName);
		if(documentEntry == null) {
			throw new Exception("It's not posible find the document " + 
					documentName + " into folder " + companyId);
		}
		/**Se extrae el contenido del fichero a descargar.*/
		MediaContent mc = new MediaContent();
		String exportURL = EXPORT_URL + "?id=" + documentEntry.getDocId()+ "&format=doc";;
		mc.setUri(exportURL);
		MediaSource ms = client.getMedia(mc);
		InputStream inStream = null;
		try {
			inStream = ms.getInputStream();
			int c;
			while ((c = inStream.read()) != -1) {
				outputStream.write(c);
			}
		} finally {
			if (inStream != null) {
				inStream.close();
			}
			if (outputStream != null) {
				outputStream.flush();
				outputStream.close();
			}
		}
	}
	
	public void addDocument(String companyId, 
			String documentName, byte[] content) 
				throws Exception {		
		synchronized(this) {
			if(client==null) {
				client = new DocsService(applicationId);
				client.setUserCredentials(username, password);
			}
		}
		/**Se localiza el directorio de destino*/
		URL feedUri = new URL(REPOSITORY_URL + URL_CATEGORY_FOLDER);
		DocumentQuery query = new DocumentQuery(feedUri);
		query.setTitleQuery(companyId);
		query.setTitleExact(true);
		query.setMaxResults(1);
		DocumentListFeed feed = client.getFeed(query, DocumentListFeed.class);
		if(feed.getEntries().size()==0) {
			throw new Exception("Folder " + companyId + " NOT found in folder ");
		}
		DocumentListEntry folderEntry = feed.getEntries().get(0);
		String destFolderUrl = ((MediaContent) folderEntry.getContent()).getUri();
		/** Se genera la entrada para el nuevo documento.*/
		DocumentListEntry newEntry = new DocumentEntry();
		newEntry.setTitle(new PlainTextConstruct(documentName));		
		/** Se agrega el contenido al nuevo documento creado.*/
		String mimeType = DocumentListEntry.MediaType.fromFileName(documentName).getMimeType();
		newEntry.setMediaSource(new MediaByteArraySource(content, mimeType));
		client.insert(new URL(destFolderUrl), newEntry);
	}
	
	
	public void updateDocument(String companyId, 
			String documentName, byte[] content) 
			throws Exception {
		synchronized(this) {
			if(client==null) {
				client = new DocsService(applicationId);
				client.setUserCredentials(username, password);
			}
		}
		DocumentListEntry documentEntry = this.findDocumentEntry(companyId, 
				documentName);
		documentEntry.delete();
		this.addDocument(companyId, documentName, content);
		/**
		 * This code does not work with new style documets of google docs. Visit this url,
		 * http://code.google.com/p/gdata-issues/issues/detail?id=2166
		 * 
		 * String mimeType = DocumentListEntry.MediaType.fromFileName(documentName).getMimeType();
		 * documentEntry.setMediaSource(new MediaByteArraySource(content, mimeType));
		 * documentEntry.updateMedia(true);
		 * ******************************************************************************/
	}
	
	public void removeDocument(String companyId, 
			String documentName) throws Exception {
		synchronized(this) {
			if(client==null) {
				client = new DocsService(applicationId);
				client.setUserCredentials(username, password);
			}
		}
		DocumentListEntry documentEntry = this.findDocumentEntry(companyId, 
				documentName);
		if(documentEntry!=null) {
			documentEntry.delete();
		}
	}
	
	
	private DocumentListEntry findDocumentEntry(String folderName, 
			String documentName) throws IOException, ServiceException {
		synchronized(this) {
			if(client==null) {
				client = new DocsService(applicationId);
				client.setUserCredentials(username, password);
			}
		}
		/**Se localiza el directorio de destino*/
		URL feedUri = new URL(REPOSITORY_URL + URL_CATEGORY_FOLDER);
		DocumentQuery query = new DocumentQuery(feedUri);
		query.setTitleQuery(folderName);
		query.setTitleExact(true);
		query.setMaxResults(1);
		DocumentListFeed feed = client.getFeed(query, DocumentListFeed.class);
		if(feed.getEntries().size()==0) {
			return null;
		}
		DocumentListEntry folderEntry = feed.getEntries().get(0);
		String destFolderUrl = ((MediaContent) folderEntry.getContent()).getUri();
		query = new DocumentQuery(new URL(destFolderUrl));
		query.setTitleQuery(documentName);
		query.setTitleExact(true);
		query.setMaxResults(1);
		feed = client.getFeed(query, DocumentListFeed.class);
		if(feed.getEntries().size()!=0) {
			DocumentListEntry entry = feed.getEntries().get(0);
			return entry;
		}
		return null;
	}
	
	public void downloadFile(String exportUrl, OutputStream outputStream)
		throws IOException, MalformedURLException, ServiceException {
		synchronized(this) {
			if(client==null) {
				client = new DocsService(applicationId);
				client.setUserCredentials(username, password);
			}
		}
		MediaContent mc = new MediaContent();
		mc.setUri(exportUrl);
		MediaSource ms = client.getMedia(mc);
		InputStream inStream = null;
		try {
			inStream = ms.getInputStream();
			int c;
			while ((c = inStream.read()) != -1) {
				outputStream.write(c);
			}
		} finally {
			if (inStream != null) {
				inStream.close();
			}
			if (outputStream != null) {
				outputStream.flush();
				outputStream.close();
			}
		}
	}
	
	public void showAllDocs() throws IOException, ServiceException {
		synchronized(this) {
			if(client==null) {
				client = new DocsService(applicationId);
				client.setUserCredentials(username, password);
			}
		}
		URL feedUri = new URL(REPOSITORY_URL);
		DocumentListFeed feed = client.getFeed(feedUri, DocumentListFeed.class);
		for (DocumentListEntry entry : feed.getEntries()) {
			printDocumentEntry(entry);
		}
	}

	public void printDocumentEntry(DocumentListEntry doc) throws AuthenticationException {
		synchronized(this) {
			if(client==null) {
				client = new DocsService(applicationId);
				client.setUserCredentials(username, password);
			}
		}
		String resourceId = doc.getResourceId();
		String docType = resourceId.substring(0, resourceId.lastIndexOf(':'));

		System.out.println("'" + doc.getTitle().getPlainText() + "' ("
				+ docType + ")");
		System.out.println("  link to Google Docs: "
				+ doc.getHtmlLink().getHref());
		System.out.println("  Doc id: " + doc.getDocId());
		System.out.println("  resource id: " + resourceId);

		// print the parent folder the document is in
		if (!doc.getFolders().isEmpty()) {
			System.out.println("  in folder: " + doc.getFolders());
		}

		// print the timestamp the document was last viewed
		DateTime lastViewed = doc.getLastViewed();
		if (lastViewed != null) {
			System.out.println("  last viewed: " + lastViewed.toString());
		}

		// print who made that modification
		LastModifiedBy lastModifiedBy = doc.getLastModifiedBy();
		if (lastModifiedBy != null) {
			System.out.println("  updated by: " + lastModifiedBy.getName()
					+ " - " + lastModifiedBy.getEmail());
		}

		// print other useful metadata
		System.out.println("  last updated: " + doc.getUpdated().toString());
		System.out.println("  viewed by user? " + doc.isViewed());
		System.out.println("  writersCanInvite? "
				+ doc.isWritersCanInvite().toString());
		System.out.println("  hidden? " + doc.isHidden());
		System.out.println("  starrred? " + doc.isStarred());
		System.out.println();
	}

}
